package com.enlighten.featureExtractor;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.imageio.ImageIO;

import com.enlighten.featureExtractor.coinFeatureExtractor.RadiusBasedCoinFeatureExtractor;
import com.enlighten.featureExtractor.distances.MeanDsvStdDistance;

public class ModelExtractorMain {

	static Map<String,Map<String,List<FileWithRadiusBasedCoin>>> map = new HashMap<String, Map<String,List<FileWithRadiusBasedCoin>>>();
	
	public static void main(String[] args) throws IOException {
		if ( args.length != 1 ){
			System.out.println("Please indicate a directory from where to read the images");
			return;
		}
		
		File directory = new File(args[0]);
		try {
			validate(directory);
		} catch (Exception e) {
			e.printStackTrace();
			return;
		}
		
		RadiusBasedCoinFeatureExtractor radiusBasedCoinFeatureExtractor = new RadiusBasedCoinFeatureExtractor();
		extractFrom(directory, radiusBasedCoinFeatureExtractor);
	}
	
	public static void extractFrom(File directory, ICoinFeatureExtractor extractor) throws IOException {
		ICoinFeatureExtractor radiusBasedCoinFeatureExtractor = extractor;
		
		for(File subDirectory:directory.listFiles(new DirectoryFileFilter())){
			String coinName = subDirectory.getName();
			for(File subSubDirectory:subDirectory.listFiles(new DirectoryFileFilter())){
				String coinSide = subSubDirectory.getName();
				for(File coinImageFile:subSubDirectory.listFiles(new ImageFileFilter())){
					BufferedImage coinImage = ImageIO.read(coinImageFile);
					ICoin coinSideFeatures = radiusBasedCoinFeatureExtractor.extractFrom(coinImage);
					addCoinSideInfo(coinName, coinSide, coinImageFile.getName(), coinSideFeatures);
				}
				List<FileWithRadiusBasedCoin> list = getListFor(coinName,coinSide);
				if ( list == null )
					continue;
				FileWithRadiusBasedCoin modelToUse = getModelForList(list);
				System.out.println(subDirectory.getName() + "/" + subSubDirectory.getName() + "/" + modelToUse);
			}
		}
	}
	
	private static List<FileWithRadiusBasedCoin> getListFor(String coinName, String coinSide) {
		Map<String, List<FileWithRadiusBasedCoin>> map2 = map.get(coinName);
		if ( map2 == null )
			return null;
		
		return map2.get(coinSide);
	}

	private static FileWithRadiusBasedCoin getModelForList(List<FileWithRadiusBasedCoin> list) {
		double minDsvStd = Double.MAX_VALUE;
		FileWithRadiusBasedCoin currentModel = null;
		for(FileWithRadiusBasedCoin coinAsModel:list){
			MeanDsvStdDistance distance = new MeanDsvStdDistance();
			for(FileWithRadiusBasedCoin otherCoin:list){
				if ( !coinAsModel.equals(otherCoin) ){
					MeanDsvStdDistance mDistance = (MeanDsvStdDistance) coinAsModel.getCoin().distanceTo(otherCoin.getCoin());
					distance.addData(mDistance.getMean());
				}
			}
			distance.calculate();
			System.out.println(coinAsModel+" - mean distance : "+distance.getMean()+" - distance standard desviation : "+distance.getDsvStd());
			if ( distance.getDsvStd() < minDsvStd ){
				minDsvStd = distance.getDsvStd();
				currentModel = coinAsModel;
			}
		}
		return currentModel;
	}

	private static void addCoinSideInfo(String name, String side, String fileName, ICoin coinSideFeatures){
		Map<String, List<FileWithRadiusBasedCoin>> coinMap = map.get(name);
		if ( coinMap == null){
			coinMap = new HashMap<String, List<FileWithRadiusBasedCoin>>();
			map.put(name, coinMap);
		}
		
		List<FileWithRadiusBasedCoin> sideList = coinMap.get(side);
		if ( sideList == null){
			sideList = new ArrayList<FileWithRadiusBasedCoin>();
			coinMap.put(side, sideList);
		}
		sideList.add(new FileWithRadiusBasedCoin(coinSideFeatures, fileName));
	}

	private static void validate(File f) throws Exception {
		if ( !f.isDirectory() ){
			throw new Exception("File is not a directory");
		}
		if ( !f.canRead() ){
			throw new Exception("Directory is not readable");
		}
	}
}

class DirectoryFileFilter implements FileFilter{

	@Override
	public boolean accept(File pathname) {
		return pathname.isDirectory() && pathname.canRead() && pathname.exists() && !pathname.getName().equals(".svn");
	}
	
}

class ImageFileFilter implements FileFilter{

	@Override
	public boolean accept(File pathname) {
		boolean validFile = pathname.isFile() && pathname.canRead() && pathname.exists();
		if ( !validFile )
			return false;
		
		String name = pathname.getName();
		return name.endsWith("jpg") || name.endsWith("jpeg") || name.endsWith("png");
	}
	
}

class FileWithRadiusBasedCoin{
	ICoin coin;

	String fileName;
	
	public FileWithRadiusBasedCoin(ICoin coinSideFeatures, String fileName) {
		this.coin = coinSideFeatures;
		this.fileName = fileName;
	}
	
	public ICoin getCoin() {
		return coin;
	}
	
	public String getFileName() {
		return fileName;
	}
	
	@Override
	public String toString() {
		return fileName + " - " +coin;
	}
}